home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / tarsrc Folder / list.c < prev    next >
Text File  |  1994-02-02  |  7KB  |  345 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modified by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * List a tar archive.
  8.  *
  9.  * Also includes support routines for reading a tar archive.
  10.  *
  11.  * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
  12.  *
  13.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  14.  */
  15. #include "tar.h"
  16.  
  17. char *ctime();                /* From libc.a */
  18.  
  19. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  20. #define isspace(c)    ((c) == ' ')
  21.  
  22. long FromOct();            /* Decode octal number */
  23.  
  24. union record *head;        /* Points to current archive header */
  25. struct stat {
  26.     long    st_size;
  27.     long    st_mtime;
  28. } hstat;            /* Fake stat struct for compat. */
  29.  
  30. void    PrintHeader();
  31. void    ReadAnd();
  32. Boolean    ListArchive(), SkipFile();
  33.  
  34. /*
  35.  * List - list an archive file
  36.  */
  37. List() {
  38.     Boolean        oldAutoPage = pref.autoPage;
  39.  
  40.     if (OpenArchive("\pList Archive:", true))
  41.         return;
  42.         
  43.     if (WindInit())
  44.         return;
  45.         
  46.     pref.autoPage = true;
  47.     TextFace(underline);
  48.     WPrintf(header);
  49.     TextFace(0);
  50.     ReadAnd(ListArchive);
  51.     CloseArchive();
  52.     WindEnd(true);
  53.     pref.autoPage = oldAutoPage;
  54. }
  55.  
  56. /*
  57.  * Main loop for reading an archive.
  58.  */
  59. void
  60. ReadAnd(doSomething)
  61. Boolean (*doSomething)();
  62. {
  63.     int         status = 1;
  64.     int         prevStatus;
  65.     Boolean        errFound = false;
  66.     EventRecord    e;
  67.     CursHandle    cursor;
  68.  
  69.     if ((cursor = GetCursor(watchCursor)) != nil)
  70.         SetCursor(*cursor);
  71.  
  72.     while (!errFound) {
  73.         if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
  74.             if (
  75.                 (e.modifiers & cmdKey) &&
  76.                 ((e.message & charCodeMask) == '.')
  77.             ) {
  78.                 WaitNextEvent(keyDownMask, &e, 0L, nil);
  79.                 break;
  80.             }
  81.         }
  82.  
  83.         prevStatus = status;
  84.         status = ReadHeader();
  85.         switch (status) {
  86.         case 1:            /* Valid header */
  87.             /* We should decode next field (mode) first... */
  88.             /* Ensure incoming names are null terminated. */
  89.             head->header.name[NAMSIZ-1] = '\0';
  90.             errFound = (*doSomething)();
  91.             continue;
  92.  
  93.             /*
  94.              * If the previous header was good, tell them
  95.              * that we are skipping bad ones.
  96.              */
  97.         case 0:            /* Invalid header */
  98.         case0:
  99.             UseRec(head);
  100.             if (prevStatus == 1) {
  101.                 PgmAlert("\pReadAnd",
  102.                     "\pSkipping to next file header...",
  103.                     nil);
  104.             }
  105.             continue;
  106.  
  107.         case 2:            /* Block of zeroes */
  108.         case (int) EOF:        /* End of archive */
  109.             break;
  110.         }
  111.         break;
  112.     }
  113.  
  114.     SetCursor(&qd.arrow);
  115. }        
  116.  
  117.  
  118. /*
  119.  * Print a header record, based on tar options.
  120.  */
  121. Boolean
  122. ListArchive()
  123. {
  124.     long t;
  125.  
  126.     /* Save the record */
  127.     SaveRec(&head);
  128.  
  129.     /*
  130.      * Print the header record.
  131.      * Don't sling the names too fast!
  132.      */
  133.     PrintHeader();
  134.     if (!pref.autoPage)
  135.         Delay(60L, &t);
  136.  
  137.     /* Skip past it in the archive */
  138.     SaveRec((union record **) 0);    /* Unsave it */
  139.     UseRec(head);
  140.  
  141.     /* Skip to the next header on the archive */
  142.     return(SkipFile((long)hstat.st_size));
  143. }
  144.  
  145.  
  146. /*
  147.  * Read a record that's supposed to be a header record.
  148.  * Return its address in "head", and if it is good, the file's
  149.  * size in hstat.st_size.
  150.  *
  151.  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
  152.  * 2 for a block full of zeros (EOF marker).
  153.  *
  154.  * You must always userec(head) to skip past the header which this
  155.  * routine reads.
  156.  */
  157. int
  158. ReadHeader()
  159. {
  160.     register int    i;
  161.     register long    sum, recsum;
  162.     register char    *p;
  163.     register union record *header;
  164.  
  165.     header = FindRec();
  166.     head = header;        /* This is our current header */
  167.     if (header == nil)
  168.         return(EOF);
  169.  
  170.     recsum = FromOct(8,  header->header.chksum);
  171.     sum = 0;
  172.     p = header->charptr;
  173.     for (i = sizeof(*header); --i >= 0;) {
  174.         /*
  175.          * We can't use unsigned char here because of old compilers,
  176.          * e.g. V7.
  177.          */
  178.         sum += 0xFF & *p++;
  179.     }
  180.  
  181.     /* Adjust checksum to count the "chksum" field as blanks. */
  182.     for (i = sizeof(header->header.chksum); --i >= 0;)
  183.         sum -= 0xFF & header->header.chksum[i];
  184.     sum += ' ' * sizeof(header->header.chksum);
  185.  
  186.     if (sum == recsum) {
  187.         /*
  188.          * Good record.  Decode file size and return.
  189.          */
  190.         hstat.st_size = FromOct(1+12, header->header.size);
  191.         return(1);
  192.     }
  193.  
  194.     if (sum == 8 * ' ') {
  195.         /*
  196.          * This is a zeroed block...whole block is 0's except
  197.          * for the 8 blanks we faked for the checksum field.
  198.          */
  199.         return(2);
  200.     }
  201.  
  202.     return(0);
  203. }
  204.  
  205. /* 
  206.  * Decode things from a file header record into a "struct stat".
  207.  *
  208.  * read_header() has already decoded the checksum and length, so we don't.
  209.  *
  210.  * If wantug != 0, we want the uid/group info decoded from Unix Standard
  211.  * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
  212.  */
  213. DecodeHeader(header, st, wantug)
  214. register union record    *header;
  215. register struct stat    *st;
  216. int    wantug;
  217. {
  218. #pragma unused(wantug)
  219.  
  220.     st->st_mtime = FromOct(1+12, header->header.mtime);
  221. }
  222.  
  223. /*
  224.  * Quick and dirty octal conversion.
  225.  *
  226.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  227.  */
  228. long
  229. FromOct(digs, where)
  230. register int    digs;
  231. register char    *where;
  232. {
  233.     register long    value;
  234.  
  235.     while (isspace(*where)) {        /* Skip spaces */
  236.         where++;
  237.         if (--digs <= 0)
  238.             return(-1);        /* All blank field */
  239.     }
  240.  
  241.     value = 0;
  242.     while (digs > 0 && isodigit(*where)) {    /* Scan til nonoctal */
  243.         value = (value << 3) | (*where++ - '0');
  244.         --digs;
  245.     }
  246.  
  247.     if (digs > 0 && *where && !isspace(*where))
  248.         return(-1);            /* Ended on non-space/nul */
  249.  
  250.     return(value);
  251. }
  252.  
  253. /*
  254.  * Actually print it.
  255.  */
  256. #define    UGSWIDTH    9    /* min width of User, group, size */
  257. #define    DATEWIDTH    19    /* Last mod date */
  258. static int ugswidth = UGSWIDTH;    /* Max width encountered so far */
  259.  
  260. void
  261. PrintHeader()
  262. {
  263.     char mode;
  264.     char *timestamp;
  265.     char size[12];        /* Holds a formatted long */
  266.     long longie;        /* To make ctime() call portable */
  267.     int    pad;
  268.  
  269.     DecodeHeader(head, &hstat, 0);
  270.     /* File type and mode */
  271.     mode = '?';
  272.     switch (head->header.linkflag) {
  273.     case LF_NORMAL:
  274.     case LF_OLDNORMAL:
  275.         mode = 'F'; 
  276.         if ('/' == head->header.name[strlen(head->header.name)-1])
  277.             mode = 'D';
  278.         break;
  279.  
  280.     case LF_DIR:
  281.         mode = 'D';
  282.         break;
  283.     }
  284.  
  285.     /* 
  286.      * Convert to Mac based time from Unix based time.
  287.      */
  288.     longie = hstat.st_mtime + TIMEDIFF;
  289.  
  290.     timestamp = ctime(&longie);
  291.     timestamp[16] = '\0';
  292.     timestamp[24] = '\0';
  293.  
  294.     /* Format the file size or major/minor device numbers */
  295.     switch (head->header.linkflag) {
  296.     default:
  297.         (void) sprintf(size, "?????");
  298.         break;
  299.  
  300.     case LF_DIR:
  301.         (void) sprintf(size, "%.*s", UGSWIDTH, "");
  302.         break;
  303.  
  304.     case LF_OLDNORMAL:
  305.     case LF_NORMAL:
  306.         (void) sprintf(size, "%ld", hstat.st_size);
  307.         break;
  308.     }
  309.  
  310.  
  311.     /* Figure out padding and print the whole line. */
  312.     pad = strlen(size) + 1;
  313.     if (pad > ugswidth)
  314.         ugswidth = pad;
  315.  
  316.     WPrintf("%c %*s%s %s %s %.*s", mode, ugswidth - pad, "", size,
  317.         timestamp+4, timestamp+20, sizeof(head->header.name),
  318.                   head->header.name);
  319. }
  320.  
  321. /*
  322.  * Skip over <size> bytes of data in records in the archive.
  323.  */
  324. Boolean
  325. SkipFile(size)
  326. register long size;
  327. {
  328.     union record *x;
  329.  
  330.     while (size > 0) {
  331.         x = FindRec();
  332.         if (x == nil) {    /* Check it... */
  333.             PgmAlert("\pSkipFile",
  334.                 "\pUnexpected EOF on archive file",
  335.                 nil);
  336.             return(true);
  337.         }
  338.         
  339.         UseRec(x);
  340.         size -= RECORDSIZE;
  341.     }
  342.     
  343.     return(false);
  344. }
  345.